﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Extend.Pool;
using System;

public class PoolManager : MonoSingleton<PoolManager>
{
    private Dictionary<TypeNamePair, PoolBase> _unityPoolMap = new Dictionary<TypeNamePair, PoolBase>();
    private Dictionary<Type, ReferencePool> _referencePoolMap = new Dictionary<Type, ReferencePool>();

    #region Unity 池 

    /// <summary>
    /// 创建Gameobject池
    /// </summary>
    /// <param name="original">池元素</param>
    /// <param name="name">池名字，默认为元素物体名</param>
    /// <param name="parent">元素父物体</param>
    /// <param name="capacity">容量</param>
    /// <returns></returns>
    public GameObjectPool CreateUnityPool(GameObject original, string name = "", Transform parent = null, int capacity = int.MaxValue)
    {
        if (original == null)
        {
            throw new System.ArgumentNullException("original");
        }
        //Gameobject必须有一个名字来进行区别
        if (string.IsNullOrEmpty(name))
        {
            name = original.name;
        }
        TypeNamePair typeNamePair = new TypeNamePair(typeof(GameObject), name);
        if (_unityPoolMap.TryGetValue(typeNamePair, out var pool))
        {
            Debug.LogError($"该对象池已经存在，不能重复创建：{typeNamePair.ToString()}");
        }
        else
        {
            pool = GameObjectPool.Create(original, parent, capacity);
            pool.Name = typeNamePair.ToString();
            _unityPoolMap.Add(typeNamePair, pool);
        }
        return pool as GameObjectPool;
    }

    /// <summary>
    /// 创建Gameobject池
    /// </summary>
    /// <param name="sourcePath">资源路径</param>
    /// <param name="name">池名字，默认为元素物体名</param>
    /// <param name="parent">元素父物体</param>
    /// <param name="capacity">容量</param>
    /// <returns></returns>
    public GameObjectPool CreateUnityPool(string sourcePath, string name = "", Transform parent = null, int capacity = int.MaxValue)
    {
        if (string.IsNullOrEmpty(name))
        {
            name = sourcePath;
        }
        MDebug.Log(this, $"CreateUnityPool {sourcePath}");
        return CreateUnityPool(Resources.Load<GameObject>(sourcePath), name, parent, capacity);
    }

    /// <summary>
    /// 获取GameObject池
    /// </summary>
    /// <param name="name">若创建时未指定，则使用：original.name</param>
    /// <returns></returns>
    public GameObjectPool GetUnityPool(string name)
    {
        TypeNamePair typeNamePair = new TypeNamePair(typeof(GameObject), name);
        if (_unityPoolMap.TryGetValue(typeNamePair, out var pool))
        {
            return pool as GameObjectPool;
        }
        else
        {
            Debug.LogError("对象池不存在，请先调用【CreateGameobjectPool】创建后使用");
            return null;
        }
    }

    /// <summary>
    /// 创建组件池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="original">池元素</param>
    /// <param name="name">池名字，组件池可不传</param>
    /// <param name="parent">元素父物体</param>
    /// <param name="capacity">容量</param>
    /// <returns></returns>
    public ComponentPool<T> CreateUnityPool<T>(T original, string name = "", Transform parent = null, int capacity = int.MaxValue) where T : Component
    {
        if (original == null)
        {
            throw new System.ArgumentNullException("original");
        }
        TypeNamePair typeNamePair = new TypeNamePair(typeof(T), name);
        if (_unityPoolMap.TryGetValue(typeNamePair, out var pool))
        {
            Debug.LogError($"该对象池已经存在，不能重复创建：{typeNamePair.ToString()}");
        }
        else
        {
            pool = ComponentPool<T>.Create(original, parent, capacity);
            pool.Name = typeNamePair.ToString();
            _unityPoolMap.Add(typeNamePair, pool);
        }
        return pool as ComponentPool<T>;
    }

    /// <summary>
    /// 创建组件池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sourcePath">资源路径</param>
    /// <param name="name">池名字，组件池可不传</param>
    /// <param name="parent">元素父物体</param>
    /// <param name="capacity">容量</param>
    /// <returns></returns>
    public ComponentPool<T> CreateUnityPool<T>(string sourcePath, string name = "", Transform parent = null, int capacity = int.MaxValue) where T : Component
    {
        return CreateUnityPool<T>(Resources.Load<GameObject>(sourcePath).GetComponent<T>(), name, parent, capacity);
    }

    /// <summary>
    /// 获取组件池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    public ComponentPool<T> GetUnityPool<T>(string name = "") where T : Component
    {
        TypeNamePair typeNamePair = new TypeNamePair(typeof(T), name);
        if (_unityPoolMap.TryGetValue(typeNamePair, out var pool))
        {
            return pool as ComponentPool<T>;
        }
        else
        {
            Debug.LogError("对象池不存在，请先调用【CreateGameobjectPool】创建后使用");
            return null;
        }
    }

    /// <summary>
    /// 销毁组件/GameObject池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    /// <param name="checkNum">是否只在对象都回收后才能销毁</param>
    public void DestroyUnityPool<T>(string name = "",bool checkNum = true) 
    {
        TypeNamePair typeNamePair = new TypeNamePair(typeof(T), name);
        if (_unityPoolMap.TryGetValue(typeNamePair, out var pool))
        {
            //没有使用中的实例
            if (!checkNum || pool.Count == pool.CountUnUsed)
            {
                pool.ReleaseUnused();
                _unityPoolMap.Remove(typeNamePair);
            }
            else 
            {
                Debug.LogError($"销毁对象池失败：还有【{pool.Count - pool.CountUnUsed}】个未归还的实例");
            }
        }
        else
        {
            Debug.LogError($"销毁对象池失败：不存在该对象池 {typeNamePair.ToString()}");
        }
    }

    /// <summary>
    /// 获取所有组件/GameObject池
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="name"></param>
    public List<PoolBase> GetAllUnityPools()
    {
        List<PoolBase> list = new List<PoolBase>();
        foreach (var item in _unityPoolMap.Values)
        {
            list.Add(item);
        }
        return list;
    }

    public GameObjectPool PreloadGameObject(string sourcePath)
    {
        GameObjectPool pool;
        TypeNamePair typeNamePair = new TypeNamePair(typeof(GameObject), sourcePath);
        if (_unityPoolMap.TryGetValue(typeNamePair, out var pool0))
        {
            pool = pool0 as GameObjectPool;
        }
        else
        {
            pool = CreateUnityPool(sourcePath);
        }
        if (pool.CountUnUsed == 0)
        {
            pool.Recycle(pool.Spawn());
        }
        return pool;
    }

    #endregion

    #region 引用类型 池

    public T SpawnReferenceInstance<T>() where T : class
    {
        Type type = typeof(T);
        ReferencePool pool;
        lock (_referencePoolMap)
        {
            if (!_referencePoolMap.TryGetValue(type, out pool))
            {
                pool = new ReferencePool(type);
                _referencePoolMap.Add(type, pool);
            }
        }        
        return pool.Spawn<T>();
    }

    public void RecycleReferenceInstance<T>(T obj) where T : class
    {
        Type type = typeof(T);
        lock (_referencePoolMap)
        {
            if (_referencePoolMap.TryGetValue(type, out var pool))
            {
                pool.Recycle<T>(obj);
            }
        }
    }

    public List<T> SpawnList<T>()
    {
        return SpawnReferenceInstance<List<T>>();
    }

    public void RecycleList<T>(List<T> list)
    {
        list.Clear();
        RecycleReferenceInstance<List<T>>(list);
    }

    public List<PoolBase> GetAllReferencePools()
    {
        List<PoolBase> list = new List<PoolBase>();
        foreach (var item in _referencePoolMap.Values)
        {
            list.Add(item);
        }
        return list;
    }

    #endregion
}
